﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Xml.Linq;
using static System.Net.Mime.MediaTypeNames;

namespace SuperKit {
    class BilibiliMovie : MediaExecutor {
        // 如果配置了此项目，表示只改变Directory的名称
        public bool IsRenameOnly { get; }
        // 如果配置了此项目，表示只需要将音频提取出来并改名
        public bool IsAudioOnly { get; }
        // 如果配置了此项目，表示要删除转化后的项目
        public bool IsClear { get; }
        // 如果配置了此项目，表示要进行合并
        public bool IsCombine { get; }
        // 名称类型
        public string NameType { get; }

        public BilibiliMovie(ArgReader arg, IExecutorOwner form, string fpath) : base(arg, form, fpath) {
            IsRenameOnly = arg.IsContain("-renameOnly");
            IsAudioOnly = arg.IsContain("-audioOnly");
            IsClear = arg.IsContain("-clear");
            IsCombine = arg.IsContain("-combine");
            NameType = arg.GetParameter("-nameType", "Tab");
        }

        public override void Execute() {
            Thread t = new Thread(OnProcess);
            t.Start();
        }

        private void OnProcess() {
            DirectoryInfo dir = new DirectoryInfo(FilePath);

            var dirs = dir.GetDirectories();
            InitProcessCount(Kit.Len(dirs));
            foreach (var i in dirs) {
                try {
                    ProcessDir(i);
                } catch (Exception e) {
                    Flush(e.Message);
                }

        }

            End();
        }

        private bool IsAudio(FileInfo info) {
            using (FileStream fileStream = new FileStream(info.FullName, FileMode.Open, FileAccess.Read)) {
                using (BinaryReader binaryReader = new BinaryReader(fileStream)) {
                    // 读取整个文件到一个字节数组中
                    int read_bytes = (int)Math.Min(1024, fileStream.Length);
                    byte[] fileBytes = binaryReader.ReadBytes(read_bytes);

                    // 将目标字符串转换为字节数组
                    byte[] searchStringBytes1 = Encoding.ASCII.GetBytes("SoundHandler");
                    byte[] searchStringBytes2 = Encoding.ASCII.GetBytes("Sound Handler");
                    // 检查是否包含该字符串
                    if (ContainsString(fileBytes, searchStringBytes1) || ContainsString(fileBytes, searchStringBytes2)) {
                        //Flush($"The string 'SoundHandler' was found in the {info.FullName}.");
                        return true;
                    } else {
                        //Flush($"The string 'SoundHandler' was not found in the {info.FullName}.");
                        return false;
                    }
                }
            }
        }

        // 一个简单的方法来检查一个字节数组中是否包含另一个字节数组
        static bool ContainsString(byte[] source, byte[] toFind) {
            for (int i = 0; i <= source.Length - toFind.Length; i++) {
                bool found = true;
                for (int j = 0; j < toFind.Length; j++) {
                    if (source[i + j] != toFind[j]) {
                        found = false;
                        break;
                    }
                }
                if (found) {
                    return true;
                }
            }
            return false;
        }

        private void ProcessDir(DirectoryInfo dir) {
            Flush($"处理:{dir.Name}");

            var files = dir.GetFiles();
            FileInfo playurl = null;
            FileInfo videoInfo = null;
            FileInfo video = null;
            FileInfo audio = null;
            long size = 0;

            foreach (var i in files) {
                if (i.Name == ".playurl") {
                    playurl = i;
                } else if (i.Name == ".videoInfo") {
                    videoInfo = i;
                }

                var ext = Path.GetExtension(i.Name);
                if (ext == ".m4s") {
                    if (IsAudio(i)) {
                        audio = i;
                    } else {
                        video = i;
                    }
                    //// 当项目没有配置的时候，将视频和音频都设置为当前
                    //if (size == 0) {
                    //    video = i;
                    //    audio = i;
                    //    size = i.Length;
                    //} else {
                    //    // 如果项目已经配置，当此项目比size小，表示是音频，比size大，表示是视频
                    //    if (i.Length > size) {
                    //        video = i;
                    //    } else {
                    //        audio = i;
                    //    }
                    //}
                }
            }

            if (videoInfo == null) {
                Flush("没有发现.videoInfo文件：{0}", dir.FullName);
                return;
            }

            string name = PickName(videoInfo);
            SetProcess(name);

            if (IsRenameOnly) {
                OnRename(dir, name);
                return;
            }

            if (IsAudioOnly) {
                OnAudio(dir, audio, name);
            } else {
                OnCombine(dir, video, audio, name);
            }
        }

        private void OnAudio(DirectoryInfo dir, FileInfo audio, string name) {

            var finalPath = Path.Combine(dir.Parent.FullName, $"{name}.mp3");
            if (audio == null) {
                Flush($"未能找到音频文件：{name}");
                return;
            }

            if (File.Exists(finalPath)) {
                Flush($"文件已经存在：{Path.GetFileName(finalPath)}");
                return;
            }

            Flush($"处理文件:{name}");
            var processedAudio = ProcessMedia(audio, finalPath);
        }

        private void OnCombine(DirectoryInfo dir, FileInfo video, FileInfo audio, string name) {
            var finalPath = Path.Combine(dir.Parent.FullName, $"{name}.mp4");
            if (File.Exists(finalPath)) {
                Flush($"文件已经存在：{Path.GetFileName(finalPath)}");
                return;
            }

            Flush($"处理文件:{name}");
            var processedVideo = ProcessMedia(video, Path.Combine(dir.FullName, $"{name}_v.mp4"));
            var processedAudio = ProcessMedia(audio, Path.Combine(dir.FullName, $"{name}_a.mp3"));

            if (IsCombine) {
                ExecuteFFMpeg($" -i \"{processedVideo.FullName}\" -i \"{processedAudio.FullName}\" -c:v copy -c:a copy \"{finalPath}\" -y");
            }

            if (IsClear) {
                File.Delete(processedAudio.FullName);
                File.Delete(processedVideo.FullName);
            }
        }

        private void OnRename(DirectoryInfo dir, string name) {
            string newName = Path.Combine(dir.Parent.FullName, name);
            if (Directory.Exists(newName)) {
                Flush($"{dir.Name} 已经存在！");
            } else {
                Directory.Move(dir.FullName, newName);
                Flush($"{dir.Name} 已经改名 {name}");
            }
            return;
        }

        private FileInfo ProcessMedia(FileInfo media, string path) {
            if (File.Exists(path)) {
                return new FileInfo(path);
            }

            FileStream fread = new FileStream(media.FullName, FileMode.Open);
            FileStream fwrite = new FileStream(path, FileMode.CreateNew);
            fread.Position = 9;
            byte[] buffer = new byte[1024];
            while (true) {
                int read = fread.Read(buffer, 0, buffer.Length);
                if (read == 0) {
                    break;
                }
                fwrite.Write(buffer, 0, read);
            }
            fread.Close();
            fwrite.Close();
            return new FileInfo(path);
        }

        private string PickNameByType(string text, string type) {
            string mark = $"\"{type}\":\"";
            string quote = "\"";
            var start = text.IndexOf(mark);
            if (start == -1) {
                return "";
            }
            var end = text.IndexOf(quote, start + mark.Length);
            if (end == -1) {
                return "";
            }

            string name = text.Substring(start + mark.Length, end - (start + mark.Length));
            if (name.Length > 64) {
                name = name[..64];
            }
            return name;
        }

        private int FindPage(string text) {
            string pattern = "\"p\":(\\d+)";
            Match match = Regex.Match(text, pattern);
            if (match.Success) {
                return int.Parse(match.Groups[1].Value);
            } else {
                return -1;
            }
        }

        private string PickName(FileInfo videoInfo) {
            string text = File.ReadAllText(videoInfo.FullName);
            string name = "";

            if (Kit.IsEqual(NameType, "Title")) {
                name = PickNameByType(text, "title");
            } else if (Kit.IsEqual(NameType, "Tab")) {
                name = PickNameByType(text, "tabName");
            } else if (Kit.IsEqual(NameType, "Group")) {
                name = PickNameByType(text, "groupTitle");
            } else {
                string groupTitle = PickNameByType(text, "groupTitle");
                int page = FindPage(text);
                if (!Kit.IsEmpty(groupTitle)) {
                    if (page == -1) {
                        name += $"{groupTitle} - ";
                    } else {
                        name += $"{groupTitle}[P{page:D3}] - ";
                    }
                }

                string title = PickNameByType(text, "title");
                string tab = PickNameByType(text, "tabName");
                if (Kit.IsEqual(title, tab)) { // 当两个是一样的时候，只选一个就好
                    name += title;
                } else {
                    name += $"{title} - {tab}";
                }
            }

            char[] invalidChars = Path.GetInvalidFileNameChars();
            // 创建一个StringBuilder用于构建合法的文件名
            StringBuilder validFileName = new StringBuilder(name);
            // 替换所有非法字符为下划线
            foreach (char invalidChar in invalidChars) {
                validFileName.Replace(invalidChar.ToString(), "_");
            }

            return validFileName.ToString();
        }
    }
}
